Skip to content

fork: pkgsite 内网部署 patch 集(base-path / markdown ext / multi-repo / view source)#2

Open
NickWilde18 wants to merge 26 commits into
masterfrom
fork/main
Open

fork: pkgsite 内网部署 patch 集(base-path / markdown ext / multi-repo / view source)#2
NickWilde18 wants to merge 26 commits into
masterfrom
fork/main

Conversation

@NickWilde18
Copy link
Copy Markdown
Owner

Summary

Long-lived fork branch——所有 pkgsitex 自定义改动累积在此分支。永不 merge 到 master——master 保持跟上游 `golang/pkgsite` 同步,让 GitHub "Sync fork" 永远 fast-forward 0 conflict。

替代被 GitHub 自动关掉的 PR #1(rename feat/base-path-support → fork/main 删除老 branch 触发 close)。Patch 范围已远超 PR #1 标题"base-path"。

Patch 集合

1. URL base path 支持

-base-path=/pkgsitex flag 让站点整体挂子路径下,反代 / 共享域名场景。空 = 默认挂根,跟上游零差异。

涉及:mux pattern prefix、http.StripPrefix、template helper(abs / basepath / asset)、TS runtime base path、godoc cross-reference URL、view source /files/ prefix、detail handler trailing-slash 死循环修。

2. 显示未导出符号 + client toggle

-show-unexported flag → `godoc.IncludeUnexported` → `doc.AllDecls`;同时改 `internal/fetch/load.go` 的 `removeNodes` 逻辑(AST 阶段就要保留 unexported FuncDecl)。

main.ts client-side toggle button:默认隐藏私有 declaration / index / 侧边栏链接,点 button 切显示,localStorage 跨页保留。Method id (`Type.method`) 取最后段判私有;`pkg-` / `section-` / `hdr-` 前缀的页面 anchor 排除避免误标。

3. dochtml markdown extensions

godoc 注释走 `go/doc/comment` parser 不识别 markdown 反引号 / bold / fenced code——用户写 doc.go 时这些已成习惯。post-process HTML 加:

  • ```text``` → text
  • texttext(容许跨段内换行 + 字符上限防贪心跨段)
  • ```mermaid 围栏 →
    ...

`

` 内字符按字面不改(Go struct tag literal 等不被误转)。

4. Mermaid 渲染(client-side lazy load)

`` 注 + frontend.tmpl head 注 inline module script:页面有 `code.language-mermaid` 才动态 import `mermaid@10` from jsdelivr CDN。无图页面零成本。internal/sanitizer 放行 ``/`

` 的 `class="language-X"` attr。

5. View source 本地 file mux

cmd/pkgsite local mode 走 `/files/{path}` 让 pkgsite 自带 file mux serve 源码,不需要把代码拷到 pod——直接从 mount 进来的目录读。base path 模式下 godoc `renderOptions` 加 `localPrefix` 闭包识别 `/files/` 起头的 URL prefix BasePath。GitHub 远程 URL 不动跟上游一致。

6. Dockerfile + multi-repo 启动

仓库根加 `Dockerfile`(multi-stage build pkgsite + 保留 Go toolchain runtime 给 `go/packages.Load` 用)。

cmd/pkgsite 接受多个 module path 参数,开发者本地 clone 多 repo 平级到 `~/Repo/`,命令行末尾列 path 即可全索引。

维护策略

  • master 永远 = upstream master,不 merge fork/main。GitHub "Sync fork" 永远 fast-forward
  • fork/main 月度 `git rebase upstream/master` 跟随;conflict 主集中在 `internal/frontend/server.go` 的 mux 列表和 `static/**/*.tmpl` 模板
  • AI 维护

Test plan

  • `go vet ./...` 全包 0 错
  • `go test` 全绿(除一个上游本来就 fail 的 `TestFetchModule/master_version_of_stdlib_module/stdlibzip` —— pristine upstream/master 同样 fail,stdlib pseudo-version commit hash fixture 漂移,跟 fork 无关)
  • 本地起 `pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 ` smoke:5 个 module 全索引,渲染 4 张 mermaid(含 sequenceDiagram / flowchart)、unexported toggle、view source 200、bold/code/markdown 全识别

NickWilde18 added 26 commits May 7, 2026 14:53
加 -base-path flag 让 pkgsite 整站可挂在 URL 子路径下(如
-base-path=/gogodocs,站点入口 http://host/gogodocs/)。空 = 默认挂根
路径,跟上游零差异。需求来自反代 / 子路径部署场景。

主要改动:
- cmd/pkgsite/main.go:加 -base-path flag + validateBasePath(/foo
  形式,不带尾斜杠);ServerConfig.BasePath 透传
- cmd/internal/pkgsite/server.go:ServerConfig + newServer 增加 BasePath,
  传给 frontend.NewServer
- internal/frontend/server.go:ServerConfig + Server 加 basePath 字段;
  Install() 内 shadow handle 入参注入 prefixPattern 自动给所有 mux
  pattern 加前缀("/static/" → "/gogodocs/static/"),下面的具体路由
  声明完全不动;staticHandler / 内部 StripPrefix(third_party / sitemap /
  files / detail-stats / search-stats)手动加 s.basePath 让 file server
  能剥到正确剩余 path
- internal/frontend/templates/templates.go:新增 abs / basepath template
  helper,funcsWithBasePath 在内置 funcs 上叠 closure 实现
- internal/godoc/dochtml/template.go:LoadTemplates 接受 basePath 参数,
  存包级 var;BasePath() 暴露给读取(dochtml 单进程使用,包级 state OK)
- internal/godoc/dochtml/dochtml.go:godoc cross-reference 链接生成
  (PackageURL func)拼上 basePath,让 std lib / 同模块 import 链接在
  反代环境正确路由
- internal/godoc/dochtml/{dochtml,symbol}_test.go:LoadTemplates 调用
  补空 basePath 参数
- static/**/*.{tmpl,md}:批量把 hardcoded 绝对路径改成 abs / basepath:
  - "/static/foo.svg" → "{{abs `/static/foo.svg`}}"
  - "/search?q=..." → "{{abs `/search`}}?q=..."
  - "/{{.Path}}" → "{{basepath}}/{{.Path}}"
  - "/" → "{{abs `/`}}"
  - "/vuln/{{.ID}}" → "{{basepath}}/vuln/{{.ID}}"
  约 150 处 mechanical 替换;空 basePath 时 abs / basepath 都返原样

后续 patch 还要做:Go 端 http.Redirect 5 处字面量 / inline HTML in
serrors.go / TS 端 ~10 处 + bundle rebuild。
5 处硬编码 redirect 接 base path——之前的 commit 处理了模板和 mux
但忽略了 Go 端字面量 redirect URL,反代环境下这些 redirect 会跳出
站点子路径丢失上下文。

- internal/frontend/server.go:/cmd/cgo(C 包重定向)/v1/api(×2)
  三处加 s.basePath 前缀
- internal/frontend/fetchserver/fetch.go:FetchServer struct 加 BasePath
  字段(caller 不传则空字符串 = 行为同上游)
- internal/frontend/fetchserver/404.go:stdlib shortcut redirect 和
  nested-modules 搜索 redirect 用 s.BasePath 拼前缀

未处理(暂留 TODO):
- internal/middleware/godocredirect.go:godoc.org → pkg.go.dev 跳转,
  fork 内网部署用不到,host 还是 hardcoded "pkg.go.dev"
- internal/frontend/serrors/serrors.go:inline error template
  里的 <a href="/search?q=..."> / <a href="/about#...">——
  这两个 template 自带 parse,没注入 abs func;fork 内网这两类
  错误页极少触发,待 smoke test 发现真在出现再补
- cmd/frontend/main.go:prod 二进制创建 FetchServer 没传 BasePath。
  cmd/pkgsite(dev 二进制 / docker-compose)不用 FetchServer,影响为零。
P1 收尾——让站点真正能在 /gogodocs/ 子路径下浏览(之前 commit 把
框架搭好,但 smoke test 暴露三类残留问题):

1. safehtml/template TrustedResourceURL 校验严格——abs 在 <link href>
   后接 dynamic ?version={{.AppVersionLabel}} 拼接被拒("?version="
   不是合法 URL prefix)。新增 [asset path version] template helper:
   一气拼好 base + path + ?version=<v> 返回 safehtml.TrustedResourceURL,
   单 segment 单类型让 safehtml 满意。批量 sed 把模板里
   `{{abs `path`}}?version={{.AppVersionLabel}}` 改成
   `{{asset `path` .AppVersionLabel}}` 共 ~15 处。

2. <script> 上下文里 abs 调用过不了 _sanitizeScript(要求
   safehtml.Script 类型;abs 返 string / TrustedResourceURL 都不行)。
   inline loadScript("...") 改成 runtime 从
   document.documentElement.dataset.basePath 拼,frontend.tmpl + unit/versions
   + fetch + worker/index 共 4 处。

3. detail handler (unit/std/module 详情页入口)原版假设挂根:
   - r.URL.Path == "/" 判 home → 加 basePath / basePath+"/" 覆盖
   - trailing-slash 规范化 redirect 把 "/gogodocs/" 去尾变 "/gogodocs",
     mux 又 307 反向加尾,301 ↔ 307 死循环。加 basePath self 例外
     避免去尾;
   - urlinfo.ExtractURLPathInfo 假设 "/<module>" 形式,要先 strip basePath
   - stdlibRedirectURL 返 "/std" 等挂根 path,redirect 时补 basePath

4. TS 端 ~10 处硬编码 path(playground.ts 3 处 fetch、carousel.ts 2 处
   svg src、about/index.ts 1 处 svg + 1 处 location 比较),新增
   static/shared/base-path/base-path.ts 提供 getBasePath() / abs()。
   静态模板 frontend.tmpl 给 <html> 注入 data-base-path="{{basepath}}"
   作 runtime source of truth。重跑 esbuild 把 base-path 模块编进所有
   bundle (frontend.js / unit/main.js / about/index.js)。

5. 模板 funcs return type 改回 plain string——尝试过返
   safehtml.TrustedResourceURL 让 link href 通过,但 <script> 内同时
   要 safehtml.Script,单类型不能两全;plain string 让 safehtml
   按 context 自动 escape 是上游惯例。

Smoke test (本地 docker-compose 还没起,先跑 cmd/pkgsite 二进制 -base-path=/gogodocs):
- /gogodocs/                            HTTP 200 (home)
- /gogodocs/static/frontend/frontend.min.css  HTTP 200 (static)
- /gogodocs/about                       HTTP 200
- /gogodocs/search?q=fmt                HTTP 200
- /gogodocs/std                         HTTP 200 (stdlib detail)
- /gogodocs/chat.cuhksz                 HTTP 200 (本仓库 module)
- /gogodocs                             HTTP 307 → /gogodocs/(mux 自动)

未处理(fork 内网用得少,遇到再补):
- godocredirect 中间件:godoc.org 跳 pkg.go.dev,host 仍硬编码
- serrors.go inline error template 里两处 <a href="/...">
- cmd/frontend prod 二进制创建 FetchServer 没传 BasePath
P2 patch 集——给 fork 加三个内网部署常用能力,跟 P1 base path 解耦:

1. Mermaid 渲染(rsc.io/markdown 已经把 fenced code 输出
   <pre><code class="language-X">,缺最后一公里):
   - internal/sanitizer/sanitizer.go:放行 <code>/<pre> 的 class 属性,
     用新加的 langClass 正则限定只能是 "language-X" 形态,防任意
     class 注入
   - static/frontend/frontend.tmpl:head 末尾加 <script type="module">,
     页面里有 code.language-mermaid 才动态 import mermaid(~500KB)渲染。
     CDN 走 jsdelivr——公司内网若 strict-CSP / 不通公网,本地放
     third_party/mermaid 后 swap import URL 即可

2. -show-unexported flag(fork 内网 godoc 常见诉求——自家代码完整
   展示比 public-only 视图更有用):
   - internal/godoc/render.go:包级 var IncludeUnexported;DocPackage
     在 noFiltering(stdlib builtin 特例)之外多识别这个开关 → 给
     doc.NewFromFiles 传 doc.AllDecls
   - cmd/pkgsite/main.go:-show-unexported flag 直接设全局 var,
     避开 ServerConfig → frontend → godoc 多层透传(pkgsite 单
     进程一种行为,包级 state OK)

3. internal/ 包浏览:默认就 work(cmd/pkgsite 用 fetchdatasource +
   UseListedMods),不需 patch

Smoke test:
- /gogodocs/chat.cuhksz/internal/service/im/weixin HTTP 200,
  HTML 含 pollLoop / dispatchMessage / reconcileOnce / userMonitor
  等未导出符号——AllDecls 生效
- 模板注释从中文改英文(中文注释里的 \`\`\` 三反引号被 safehtml
  误判为 ES6 template literal 起始,整页 render 失败)

未做:mermaid 渲染端到端 smoke——cmd/pkgsite local mode 不走 README,
只渲 godoc comment(rsc.io/markdown 不在主路径上)。等 P0b docker-compose
跑完整 worker → DB → README 渲染流程时再实测一段含 mermaid 的 README。
cmd/pkgsite local mode 跑 docker container 用:
- multi-stage build:golang:1.24 编 cmd/pkgsite 二进制,stage 2 复用
  golang:1.24 base(不能用 alpine——local mode 走 go/packages.Load,
  container 必须自带 Go toolchain 调 `go list` 解析 module 依赖)
- ARG GOPROXY 默认 goproxy.cn,build / runtime 都生效——内网 / 国内
  网络环境的常态
- /repos 约定 mount 目录,docker-compose 把每个 Go module 仓库挂进
  这个路径下;容器主进程是 pkgsite,命令行参数指定 base-path /
  show-unexported / -http 端口 / mount 进来的 module path
- ENTRYPOINT pkgsite + CMD -h:默认行为是打 usage(防止误启动空容器
  hang);docker-compose 通过 command: 覆盖

下一步:Chat 仓库 docker-compose.dev.yml 加 gogodocs service,
mount 仓库,base-path=/gogodocs。
P1 smoke test 没覆盖到 module overview / unit detail 页的子目录链接,
用户在浏览器实测时发现 chat.cuhksz module 页只显示 api / manifest 两个
目录(internal 被 pkg.go.dev 默认 "Show internal" toggle 隐藏),
点子目录链接还会跳出 base path 外 404——两个独立 bug:

1. internal/frontend/versions/versions.go: ConstructUnitURL 是 pkgsite
   内部所有 unit/package/module 详情页链接的核心 URL builder(subdir
   列表 / search / breadcrumb 都走它),它生成的绝对路径必须带 base
   path 前缀。加包级 var BasePath,cmd/pkgsite/main.go 跟 godoc.IncludeUnexported
   同模式设置——pkgsite 单进程一种行为,不必加新参数让上百 caller 都改

2. unit.tmpl / unit/main/main.tmpl / search/search.tmpl 三处
   `loadScript('/static/...')` 用**单引号**,P1 sed 只匹配双引号
   loadScript("...") 漏过去。改成 `(document.documentElement.dataset.basePath
   || "") + "/static/..."` runtime 拼前缀(同 frontend.tmpl 内 inline
   script 模式)。修不修这个的影响:unit.js / main.js bundle 加载 404 →
   main.ts 里 "data-local=true 时 auto-click Show internal toggle 让
   internal 目录默认展开" 这段没跑 → 用户感知 internal 目录"不存在"
…old / mermaid)

go/doc/comment parser 不识别 markdown 反引号 / **bold** / fenced code,
但用户写 doc.go 时这些 markdown 风格已经形成习惯(毕竟 .md 写得多)。
渲染产物里 raw \` 和 ** 字面输出体验差,mermaid 图块也没办法变 fenced
code class 让客户端 mermaid.js 渲。

新增 internal/godoc/dochtml/internal/render/markdown_ext.go:
post-process 渲染好的 HTML 加三条 inline 转换:

- \`text\` → <code>text</code>
- **text** → <strong>text</strong>
- <pre>\`\`\`mermaid\\n...\`\`\`</pre> → <pre><code class="language-mermaid">...</code></pre>
  让 frontend.tmpl 里的 mermaid lazy-load CDN 脚本能识别并渲

实现:walkOutsidePre 把 HTML 切成 <pre> 内 / 外两类 segment,<pre> 内
原样保留——godoc 把 markdown 4 空格缩进 code block 渲染成 <pre>,
里面字符按字面(如 Go struct tag literal `json:"..."` 保持不动)。

设计选型:post-process HTML 比 fork go/doc/comment parser 干净——
保留 godoc 原生 [Symbol] cross-reference / heading-id / 链接提取等
功能不变,只在表层加几条 inline 转换。代价是 regex on HTML 不优雅,
但 godoc 渲染的 HTML 结构有限,<pre> 边界清晰,实测没遇到 corner case。

linkify.go formatDocHTML 在最后调一次 applyDocMarkdownExt 包装。

Smoke test (/gogodocs/chat.cuhksz/internal/service/im/weixin):
- <code>: 40 个,含 <code>wx:monitor:lease:{upn}</code> 等用户报告的
  反引号字面量
- <strong>: 38 个,含 <strong>不同 binding</strong> 等 bold
- language-mermaid: 2 个——doc.go 里两张 mermaid 时序图都被识别,
  浏览器侧 mermaid.js 渲成 SVG
两个 bug 一起修:

1. -show-unexported 实际没效果——godoc.IncludeUnexported=true 只让
   doc.NewFromFiles 用 doc.AllDecls,但 fetchdatasource 在喂给它之前先
   调 godoc.Package.AddFile(removeNodes=true) 在 AST 阶段就剥光未导出
   FuncDecl,doc.NewFromFiles 看到的 AST 已经被剔光 → AllDecls 救不回。
   修:internal/fetch/load.go 在 godoc.IncludeUnexported=true 时也把
   removeNodes 设 false,跟 stdlib builtin 特例同模式保留全部 nodes。
   smoke:weixin 包页面 dispatchMessage / authedClient / buildHelpText
   等私有函数现作为 data-kind="function" declaration 渲染,不再只是
   raw text 引用。

2. view source 链接没带 /gogodocs/ 前缀——cmd/pkgsite local mode 下
   source.Info 模板生成 "/files/{path}" 让 file mux serve 源码(不
   拷代码到 pod,直接从 mount 进来的目录读),但这个 URL 没经过
   ConstructUnitURL 这条已 patched 的路径。修:godoc 加 BasePath
   包级 var(同 IncludeUnexported 模式),renderOptions 里加 localPrefix
   闭包识别 "/files/" 起头的 local URL 前置 BasePath。GitHub 远程 URL
   (http:// 起头)不受影响,跟上游一致。
   smoke:view source URL 现是 "/gogodocs/files/.../poll_loop.go" 且 HTTP 200
   能正常 serve 源码内容。
两个 follow-up bug fix + UX 增强:

1. bold 跨行漏识别:boldRe 旧 pattern \`\`\\*\\*([^*\\n]+)\\*\\*\`\` 排除 \\n
   导致跨行 bold 不匹配——godoc 段落渲染保留段内 \\n,bold 写多行
   是常见用法(如 weixin/poll_loop.go 里 \`\`**Redis cursor(wx:get_updates_buf:{upn})
   只在本批所有 handler 跑完后才推进**\`\` 这种)。改成 \`\`\\*\\*([^*<]{1,500})\\*\\*\`\`
   容许 \\n + 用 [^<] 防跨 HTML 段落标签 + 500 字符上限双重防御
   贪心跨段。smoke:\`\`<strong>Redis cursor(...)只在本批所有 handler
   跑完后才推进</strong>\`\` 现正确包标签

2. unexported 符号 toggle(用户报告"私有太多反而拖慢阅读,但有时
   要看"):static/frontend/unit/main/main.ts 末尾加 IIFE,按 id
   首字母大小写自动给私有 declaration / index 链接的 wrapper 加
   .Documentation-unexported class,默认 CSS 隐藏;Index header
   旁注入 Show / Hide unexported toggle button,状态写
   localStorage 跨页保留。method id "Type.method" 取最后段判私有。
   const / var 不动(数量少且 godoc 整组渲染共享 declaration block,
   单独 hide 个别名字会破坏视觉)。
之前只标 .Documentation-index* 中央列表,左侧边栏 .go-Tree outline 没
覆盖——侧边栏私有函数链接还是显示。selector 加 '.go-Tree a[href^="#"]'。

isUnexported 加头部 anchor 排除:pkg- / section- / hdr- 起头的是页面
导航锚点(pkg-overview / section-readme / hdr-... 等),全小写但不是
Go 符号,扫到会误标整段隐藏。
P1 改 dochtml.LoadTemplates 签名加 basePath 时只补了 dochtml 包内
test(dochtml_test.go / symbol_test.go),漏了:
- internal/godoc/render_test.go 30 / 94
- internal/fetch/fetch_test.go 43

加空 basePath 第二个参数。

注:跑 fork 全 test 套件还有一个失败:TestFetchModule/master_version_of_
stdlib_module/stdlibzip 期望 commit hash 89fb59e2e920 实际 2b6a2e56778f。
切回 pristine upstream/master 复现同样 fail——stdlib zip 内容随 Go 版本
变化但 fixture 写死,是上游历史问题,跟 fork patch 无关。
P1 改这两个函数签名加 basePath 时还有 5 处漏:
- internal/worker/server.go (prod worker 二进制;fork 主走 cmd/pkgsite
  但仍要 build 通过)
- internal/worker/server_test.go
- internal/fetchdatasource/fetchdatasource_test.go
- internal/tests/templates/templates_test.go (含 ParsePageTemplates)
- internal/testing/integration/integration_test.go

加空 basePath 第二参数。go vet ./... + go test ./... 全过(除 1 个
TestFetchModule/master_version_of_stdlib_module/stdlibzip——这是上游
fixture 漂移,pristine upstream/master 同样 fail,跟 fork patch 无关)。

golangci-lint 24 issues(errcheck / staticcheck)全是上游历史问题不动。
GitHub repo 已 rename NickWilde18/gogodocs → NickWilde18/pkgsitex;
本仓库内部 17 个文件里所有 \"gogodocs\" 字面量替换为 \"pkgsitex\",覆盖:

- cmd/pkgsite/main.go:-base-path flag 默认值文案 + comment
- cmd/internal/pkgsite/server.go:ServerConfig.BasePath 注释
- internal/frontend/server.go / versions/versions.go / templates/templates.go
  / fetchserver/fetch.go:godoc 注释里举例 URL 全换
- internal/godoc/render.go / dochtml/dochtml.go / dochtml/template.go:
  PackageURL 拼接逻辑注释 + IncludeUnexported 包级 var 注释
- internal/godoc/dochtml/internal/render/markdown_ext.go:注释举例
- static/frontend/unit/main/main.ts:localStorage key
  gogodocs:showUnexported → pkgsitex:showUnexported(影响:之前
  toggle 状态用户需要重新点一次切换)
- static/frontend/about/index.ts、static/shared/base-path/base-path.ts:
  注释里举例 base path
- Dockerfile:org.opencontainers.image.source label

esbuild 已重 build 静态 bundle 同步反映 main.ts / about.ts 改动。

不动 git commit history(之前的 commit message 历史保留 \"gogodocs\"
字面量)。fork 仓库 PR #1 标题 / description 待 update。

Smoke test:本地起 \`-base-path=/pkgsitex\`,主页 + chat.cuhksz module
HTTP 200,渲染 HTML 残留 \"gogodocs\" 引用 0 个。
跟 Chat 端 PR golang#91 close 配套:fork own 启动配置,业务仓库不再放 docker-
compose / deploy/gogodocs README——这些应该跟工具仓库走,新接入仓库
(UniAuth / open-platform / ...)不需要各自维护一份 docker-compose。

- compose.yaml:multi-repo mount 默认覆盖 Chat / Doubao-Speech-Service /
  UniAuth/uniauth-gf / UniAuth/ittools_sync / open-platform 5 个内部
  module。开发者 \`cd ~/Repo/pkgsitex && docker compose up -d\` 即跑通;
  增 / 减仓库改 volumes + command 末尾即可
- README.md:完全重写从 fork 视角描述——快速启动(本地 binary 推荐 +
  docker 备选)、配置选项、增减仓库、浏览体验表(含 fork 独有的
  Show unexported toggle / mermaid 渲染 / view source 等)、patch 集
  说明、prod 部署 TODO、维护策略(master = upstream / fork/main =
  long-lived patch)、故障排查。末尾保留上游 README 引用块和 license

Chat PR golang#91 已 close(commit 提到原因:配置随工具走);Chat docker-
compose.dev.yml 这边对应 revert(撤回 gogodocs service + 删
deploy/gogodocs/ 目录)单独提一个 follow-up PR。
cmd/worker / cmd/frontend prod 模式下,让 pkgsite 内部所有 license
检查直接通过——内网仓库常见情况:私有 repo 没 LICENSE 文件、或者
LICENSE 写 \"All rights reserved\" / \"Proprietary\" 之类非 OSS license。
上游默认拒绝渲染("this package is not redistributable" page),fork
接受这种场景。

实现:internal/licenses/licenses.go 加全局 var Permissive bool。
- Detector.ModuleIsRedistributable():Permissive=true 直接返 true
- Redistributable(types):Permissive=true 直接返 true(容许空 license
  列表 + 任意 SPDX 类型)

默认 Permissive=false 跟上游零差异;prod config 显式开 permissive: true。

注:开启后 license metadata 仍被解析 + 展示在 UI——是否真对外公开仍
由 pkgsitex 的网络可达性 + UniAuth 鉴权决定。

go test ./internal/licenses/... 全绿——上游测试基于 default Permissive=false
不受影响。
prod 部署架构(跟 cmd/pkgsite local mode 解耦):postgres + athens
+ worker + frontend,加 init / migrate / netrc-writer 一次性容器
共 7 个 service。Athens 当 GOPROXY 缓存层 + 用 PAT 拉私有 GitHub repo,
worker 走 athens 而非 proxy.golang.org,frontend 从 postgres 读渲染。

文件:

- cmd/pkgsitex-init/main.go:prod stack 的 bootstrap 容器。三 mode:
  - netrc-only:只写 athens netrc(含 PAT),athens 启动前跑(避免
    athens→worker→athens 循环依赖)
  - init:等 worker /healthcheck OK + 对每个 module enqueue(versions:
    all-tags 时先 git ls-remote 拿全 tag)
  - refresh:周期 cron,跳过 netrc / wait,仅重新 ls-remote + enqueue
    新版本

- compose.prod.yaml:完整 7 service 定义。docker-compose dependency
  graph:netrc-writer → athens → worker → init;postgres → migrate →
  worker / frontend。volumes 持久 postgres-data / athens-data /
  athens-config(netrc 落 disk volume)

- deploy/prod/Dockerfile:multi-stage build worker / frontend / init
  三个 target 共享 base(debian-slim + ca-certs + git)。git 给 init
  跑 ls-remote 用

- deploy/prod/{.env.example, config.yaml, README.md}:
  .env 模板(GITHUB_TOKEN);config schema(module list + versions
  all-tags 开关 + monorepo 子模块的 repoUrl override);用户启动
  README 含架构图 + 加 / 删 module 操作 + 周期 refresh cron 配方 +
  故障排查表

- .gitignore:拦 deploy/prod/.env 防 PAT 明文 commit

未做(README TODO 列出,下一轮补):
- cmd/frontend / cmd/worker 加 -base-path / -show-unexported / -license-permissive
  flag。当前 compose 用 PKGSITEX_* env var 占位,未通到代码层
- init 容器周期 cron 内置(当前依赖 host crontab)

Smoke build:go build ./cmd/{worker,frontend,pkgsitex-init} 全过;
docker-compose 真起验证留下一轮(牵涉拉 image + 私有 repo PAT 测,
做完会更新 PR description)。
切换 prod 部署模式:从"用户本地 build"改成"用户拉 docker.io 镜像即用"。

- deploy/prod/Dockerfile:multi-stage 改成单 stage 输出三 binary
  (worker / frontend / pkgsitex-init)共用一份 base + ca-certs + git。
  减少 docker.io push 频次和拉取流量;compose 里通过 entrypoint: 选
  跑哪个

- .github/workflows/docker.yml:fork/main push 时自动 build + push
  到 docker.io/nickwilde18/pkgsitex;tag 策略 fork-main 浮动指最新
  / <sha> immutable 方便 rollback。PR 触发只 build 不 push 验证
  Dockerfile。GitHub Actions cache 加速。用户需在 fork repo Settings
  加两个 secret:DOCKERHUB_USERNAME / DOCKERHUB_TOKEN

- compose.prod.yaml:worker / frontend / netrc-writer / init 四个
  service 从 build:context: 改 image: ${PKGSITEX_IMAGE:-...:fork-main},
  pull_policy:always 自动跟新;entrypoint: 选具体 binary。env 重写
  PKGSITEX_IMAGE 切 sha / 本地 build 镜像

- README.md:Prod 部署段从 "TODO 待写" 改成完整启动指引(4 步
  copy env / edit config / up / open);强调拉 image 不需 build;
  指 deploy/prod/README.md 看完整运维

- deploy/prod/README.md 启动段改 "首次 docker pull ~30 秒";新增
  "切镜像版本 / 用本地 build" 段说明 PKGSITEX_IMAGE / PKGSITEX_PULL_POLICY
  env

下一步用户操作:
  1) https://hub.docker.com/settings/security 生 access token
  2) GitHub fork repo Settings → Actions secrets 加 DOCKERHUB_USERNAME
     / DOCKERHUB_TOKEN
  3) push fork/main 触发 workflow,docker.io 上 nickwilde18/pkgsitex
     仓库出现镜像即可在 Chat 仓库新 PR 引用

下一步我做:Chat 仓库新 PR 加 prod stack 到 docker-compose.dev.yml
(profile=pkgsitex 默认不启)+ deploy/pkgsitex/{config.yaml,.env.example}。
GitHub Actions build 失败:
  go: go.mod requires go >= 1.25.5 (running go 1.24.13; GOTOOLCHAIN=local)

升 deploy/prod/Dockerfile + 仓库根 Dockerfile 两处 FROM 到 golang:1.25。
启动 cmd/worker 时报 'template: index.tmpl:10: function "abs" not defined':
fork base-path patch 把 static/worker/index.tmpl 里 URL prefix 改 {{abs ...}},
但 internal/worker/server.go 的 FuncMap 没注册 abs。

worker UI 只给运维看不走前端反代——abs 直接 identity 返原值即可,省把
-base-path flag 重复连通到 cmd/worker。
worker / athens / frontend 三个 service 的 compose.prod.yaml healthcheck 都
用 wget 探 /healthcheck endpoint,但 debian-bookworm-slim base 默认不带 wget,
导致容器在跑但 healthcheck 永远 unhealthy。
worker mux 注册的健康路径是 /healthz([`internal/worker/server.go`]),
之前 compose healthcheck 写成 /healthcheck 永远 404 → 容器跑得起来但
status 卡在 (health: starting) 直到 retries=30 耗尽。
…真起作用

现在为止 PROD_DEPLOY 的 base-path / show-unexported / license-permissive
是 env var 占位,cmd/frontend 不读——挂 /pkgsitex 子路径完全没生效。
这次:

- cmd/frontend/main.go 加 -base-path / -show-unexported flag,照搬
  cmd/pkgsite/main.go 的语义:validateBasePath + 设 godoc.{IncludeUnexported,
  BasePath} + versions.BasePath + ServerConfig.BasePath
- compose.prod.yaml frontend command 改成传真 flag(-base-path=/pkgsitex /
  -show-unexported / -bypass_license_check),删掉旧 env var 占位

license-permissive 复用上游已有 -bypass_license_check,不再单独包装。
worker 不暴露 base-path 段(运维直连),cmd/worker 暂不加 flag——abs
template helper 走 identity 已够。
worker mux 注册的健康路径是 /healthz——init container 用 /healthcheck 永远
404,落到 ctx 超时整个流程才 fail。和前几个提交里 compose healthcheck 同
源 typo。
之前只发 linux/amd64,macOS arm64 host 直接 docker pull no manifest,
解法是 docker-compose 加 platform: linux/amd64 走 Rosetta emulation。这次
build-push-action 加 platforms: linux/amd64,linux/arm64 + setup-qemu-action,
两个 arch 都直接 native,下游 docker-compose 不需要再声明 platform 字段。
GitHub 主页和搜索结果默认抓 README.md 显示——开源仓库默认英文更适合
全球用户首次接触;中文文档对内部团队仍重要,单独分到 README.zh-CN.md
保留全部内容,两边顶部互链。

顺手把"前置 Go 1.24+" / "build 拉 golang:1.24 image"两处过期版本文案
同步成 1.25(fork c10eaff 已升 builder 到 golang:1.25)。
pkgsitex 是给 Go 开发者看自家代码 godoc 的工具——dev 路径就 \`go run\`
直接最快,所谓"给没 Go toolchain 的同事"在实务上不存在(看 godoc 的人
本来就是 Go 开发者)。同步删配套文件:

- compose.yaml(cmd/pkgsite local mode 的 docker 化包装,无人用)
- Dockerfile(同上,root 级,跟 deploy/prod/Dockerfile 是两个完全独立的 image)

deploy/prod/{compose.prod.yaml,Dockerfile} 是给"看 godoc 的同事"准备的——
ops 在内部环境部署 prod stack,全公司浏览器访问 frontend;个人不需要本地
跑 docker。

troubleshooting 表里 docker 相关 row 一并清掉。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant